package drp.snowbell.cfg.init;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import drp.snowbell.annotation.AnnotationLoader;
import drp.snowbell.annotation.AsmManager;
import drp.snowbell.cache.CacheSystem;
import drp.snowbell.cache.ICache;
import drp.snowbell.cfg.Config;
import drp.snowbell.cfg.EntityConfig;
import drp.snowbell.cfg.ModuleConfig;
import drp.snowbell.cfg.PartConfig;
import drp.snowbell.cfg.PropertyConfig;
import drp.snowbell.entity.Entity;
import drp.snowbell.entity.IEntity;
import drp.snowbell.entity.IPart;
import drp.snowbell.exception.CatchException;
import drp.snowbell.io.IResource;
import drp.snowbell.io.ResourceCenter;
import drp.snowbell.module.IModule;
import drp.snowbell.util.cfg.XmlConfigFactory;


/**
 * 模块加载器
 * 
 * @author wuhj
 * 
 */
public class ModuleLoader {
	private static ModuleLoader instance;
	private static Object lock = new Object();
	private Map<String, AsmManager> asmManagerMap;
	private Map<String, ModuleConfig> moduleConfigMap;
	private Map<String, String> moduleConfigPathMap;// 模块配置文件路径

	private ICache<String, CacheEntry> cache = CacheSystem.getCache(ModuleLoader.class.getName() + "#cache");

	private Map<String, Map<String, Class<? extends IEntity>>> entityClassList = new HashMap<String, Map<String, Class<? extends IEntity>>>();
	private Map<String, Map<String, Class<? extends IPart>>> partClassList = new HashMap<String, Map<String, Class<? extends IPart>>>();

	/**
	 * 获取单例
	 * 
	 * @return
	 */
	public static ModuleLoader getInstance() {
		if (instance == null) {
			synchronized (lock) {
				if (instance == null) {
					instance = new ModuleLoader();
					instance.moduleConfigPathMap = new HashMap<String, String>();
					Map<String, ModuleConfig> mcMap = instance.loadModules();
					Map<String, AsmManager> amMap = new HashMap<String, AsmManager>();
					for (ModuleConfig mc : mcMap.values()) {
						AsmManager am = new AsmManager(mc);
						amMap.put(mc.getName(), am);
					}
					instance.asmManagerMap = amMap;
					instance.moduleConfigMap = mcMap;
				}
			}
		}
		return instance;
	}

	/**
	 * 重载某个模块.如果模块参数改变则需要重载
	 * 
	 * @param name
	 * @return
	 */
	public synchronized boolean reloadModule(String name) {
		boolean ret = false;
		String path = moduleConfigPathMap.get(name);
		if (path != null) {
			ModuleConfig mc = loadModule(path);
			if (mc != null) {
				AsmManager am = asmManagerMap.get(name);
				if (am != null) {
					am.reload(mc);
					ret = true;
				}
			}
		}
		return ret;
	}

	/**
	 * 获取模块的管理类
	 * 
	 * @param moduleName
	 * @return
	 */
	public Class<? extends IModule> getModuleClass(String moduleName) {
		AsmManager asm = asmManagerMap.get(moduleName);
		return asm.getModuleClass();
	}

	/**
	 * 获取模块的实体Class列表,注意必须要先loadModule
	 * 
	 * @param moduleName
	 * @return
	 */
	public Collection<Class<? extends IEntity>> getEntityClassList(String moduleName) {
		Collection<Class<? extends IEntity>> ret = null;
		Map<String, Class<? extends IEntity>> map = entityClassList.get(moduleName);
		if (map != null) {
			ret = map.values();
		}
		return ret;
	}

	/**
	 * 获取模块的部件Class列表,注意必须要先loadModule
	 * 
	 * @param moduleName
	 * @return
	 */
	public Collection<Class<? extends IPart>> getPartClassList(String moduleName) {
		Collection<Class<? extends IPart>> ret = null;
		Map<String, Class<? extends IPart>> map = partClassList.get(moduleName);
		if (map != null) {
			ret = map.values();
		}
		return ret;
	}

	public Class<? extends IEntity> getEntityClass(String moduleName, String className) {
		Class<? extends IEntity> ret = null;
		Map<String, Class<? extends IEntity>> map = entityClassList.get(moduleName);
		if (map != null) {
			ret = map.get(className);
		}
		return ret;
	}

	public Class<? extends IPart> getPartyClass(String moduleName, String className) {
		Class<? extends IPart> ret = null;
		Map<String, Class<? extends IPart>> map = partClassList.get(moduleName);
		if (map != null) {
			ret = map.get(className);
		}
		return ret;
	}

	/**
	 * 加载模块
	 * 
	 * @param moduleName
	 *            模块名称
	 * @param outEntityConfigList
	 *            输出的模块包含的实体配置列表
	 * @param outPartConfigList
	 *            输出的模块包含的部件配置列表
	 */
	public synchronized void loadModule(String moduleName, Collection<EntityConfig> outEntityConfigList, Collection<PartConfig> outPartConfigList) {
		CacheEntry entry = cache.get(moduleName);
		if (entry == null) {
			entry = new CacheEntry();
			ModuleConfig mc = moduleConfigMap.get(moduleName);
			AsmManager asm = asmManagerMap.get(moduleName);
			if (mc != null && asm != null) {
				Map<String, EntityConfig> ecMap = loadEntityConfigs(mc, asm);// key为className
				Collection<PartConfig> pcList = loadPartConfigs(mc, asm);
				// for (PartConfig pc : pcList) {
				// // 注册到PartManager
				// }
				// 处理实体继承关系
				Collection<Class<? extends IEntity>> classList = asm.getEntityClassList();
				for (Class<? extends IEntity> entityClass : classList) {
					processInheritance(ecMap, entityClass);
				}
				// 添加到输出
				if (outEntityConfigList != null) {
					outEntityConfigList.addAll(ecMap.values());
				}
				//
				if (outPartConfigList != null) {
					outPartConfigList.addAll(pcList);
				}
				entry.entityConfigList = ecMap.values();
				entry.partConfigList = pcList;
			}
			cache.put(moduleName, entry);
		} else {
			// 添加到输出
			if (outEntityConfigList != null) {
				outEntityConfigList.addAll(entry.entityConfigList);
			}
			//
			if (outPartConfigList != null) {
				outPartConfigList.addAll(entry.partConfigList);
			}
		}

	}

	/**
	 * 获取模块配置列表
	 * 
	 * @return
	 */
	public Collection<ModuleConfig> getModuleConfigList() {
		return moduleConfigMap.values();
	}

	/**
	 * 处理实体的继承关系
	 * 
	 * @param ecMap
	 * @param entityClass
	 */
	private void processInheritance(Map<String, EntityConfig> ecMap, Class<?> entityClass) {
		Class<?> superClass = entityClass.getSuperclass();
		if (superClass == null) {
			// 退出递归
			return;
		} else if (superClass == Entity.class) {
			EntityConfig ec = ecMap.get(entityClass.getName());
			// 合并Entity基类的属性列表
			ec.setProperties(mergePropertyConfig(ec.getProperties(), AnnotationLoader.getBaseEntityPropertyConfigList()));
			// 退出递归
			return;
		} else if (superClass.isInterface()) {
			// 退出递归
			return;
		} else {
			// 合并PropertyConfig
			EntityConfig ec = ecMap.get(entityClass.getName());
			EntityConfig superEc = ecMap.get(superClass.getName());
			if (ec != null && superEc != null) {
				ec.setProperties(mergePropertyConfig(ec.getProperties(), superEc.getProperties()));
			}
			// 递归
			processInheritance(ecMap, superClass);
		}
	}

	/**
	 * 合并PropertyConfig
	 * 
	 * @param list
	 *            子类的属性列表
	 * @param another
	 *            要被合并的基类属性列表
	 * @return 合并后的数据,并去重复
	 */
	private Collection<PropertyConfig> mergePropertyConfig(Collection<PropertyConfig> list, Collection<PropertyConfig> another) {
		// 如果属性有重复的,以子类(list里的值)的属性为准
		Map<String, PropertyConfig> map = new HashMap<String, PropertyConfig>();
		for (PropertyConfig pc : another) {
			map.put(pc.getName(), pc);
		}
		for (PropertyConfig pc : list) {
			map.put(pc.getName(), pc);
		}
		return map.values();
	}

	/**
	 * 加载组件的配置
	 * 
	 * @param mc
	 * @param asm
	 * @return
	 */
	private Collection<PartConfig> loadPartConfigs(ModuleConfig mc, AsmManager asm) {
		Map<String, PartConfig> pcMap = new HashMap<String, PartConfig>();
		Map<String, Class<? extends IPart>> classMap = partClassList.get(mc.getName());
		if (classMap == null) {
			classMap = new HashMap<String, Class<? extends IPart>>();
			partClassList.put(mc.getName(), classMap);
		}

		Collection<Class<? extends IPart>> classList = asm.getPartClassList();
		for (Class<? extends IPart> partClass : classList) {
			PartConfig pc = AnnotationLoader.buildPartConfig(partClass);
			pcMap.put(pc.getClassName(), pc);
			// 添加到class列表
			classMap.put(pc.getClassName(), partClass);
		}

		// 从配置文件获取配置
		String[] pcPathList = mc.getPartConfigPathList();
		if (pcPathList != null) {
			for (String pcPath : pcPathList) {
				try {
					URI uri = new URI(mc.getRoot() + pcPath);
					IResource resource = ResourceCenter.getResource(uri);
					if (resource != null) {
						InputStream in = null;
						try {
							in = resource.openInputStream();
							PartConfig pc = (PartConfig) XmlConfigFactory.get().load(in);

							String className = pc.getClassName();
							if (!pcMap.containsKey(className)) {
								// 说明class没加载
								try {
									@SuppressWarnings("unchecked")
									Class<? extends IPart> partClass = (Class<? extends IPart>) asm.loadClass(className);
									// 添加到class列表
									classMap.put(className, partClass);
								} catch (ClassNotFoundException e) {
									CatchException.take(ModuleLoader.class, e);
								}
							}
							pcMap.put(pc.getClassName(), pc);
						} catch (IOException e) {
							CatchException.take(e);
						} finally {
							if (in != null) {
								try {
									in.close();
								} catch (IOException e) {
									CatchException.take(e);
								}
							}
						}
					}
				} catch (URISyntaxException e) {
					CatchException.take(e);
				}
			}
		}
		return pcMap.values();
	}

	/**
	 * 加载实体的配置
	 * 
	 * @param mc
	 * @param asm
	 * @return
	 */
	private Map<String, EntityConfig> loadEntityConfigs(ModuleConfig mc, AsmManager asm) {
		Map<String, EntityConfig> ecMap = new HashMap<String, EntityConfig>();
		Map<String, Class<? extends IEntity>> classMap = entityClassList.get(mc.getName());
		if (classMap == null) {
			classMap = new HashMap<String, Class<? extends IEntity>>();
			entityClassList.put(mc.getName(), classMap);
		}

		// 从annotation获取配置
		Collection<Class<? extends IEntity>> classList = asm.getEntityClassList();
		for (Class<? extends IEntity> entityClass : classList) {
			EntityConfig ec = AnnotationLoader.buildEntityContig(entityClass);
			ecMap.put(ec.getClassName(), ec);
			// 添加到class列表
			classMap.put(ec.getClassName(), entityClass);
		}
		// 从配置文件获取配置,如果有重复将覆盖annotation里的配置
		String[] ecPathList = mc.getEntityConfigPathList();
		if (ecPathList != null) {
			for (String ecPath : ecPathList) {
				try {
					URI uri = new URI(mc.getRoot() + ecPath);
					IResource resource = ResourceCenter.getResource(uri);
					if (resource != null) {
						InputStream in = null;
						try {
							in = resource.openInputStream();
							EntityConfig ec = (EntityConfig) XmlConfigFactory.get().load(in);
							String className = ec.getClassName();
							if (!ecMap.containsKey(className)) {
								// 说明class没加载
								try {
									@SuppressWarnings("unchecked")
									Class<? extends IEntity> entityClass = (Class<? extends IEntity>) asm.loadClass(className);
									// 添加到class列表
									classMap.put(className, entityClass);
								} catch (ClassNotFoundException e) {
									CatchException.take(ModuleLoader.class, e);
								}
							}
							ecMap.put(className, ec);
						} catch (IOException e) {
							CatchException.take(e);
						} finally {
							if (in != null) {
								try {
									in.close();
								} catch (IOException e) {
									CatchException.take(e);
								}
							}
						}
					}
				} catch (URISyntaxException e) {
					CatchException.take(e);
				}
			}
		}
		return ecMap;
	}

	/**
	 * 加载所有模块的配置
	 * 
	 * @return
	 */
	private Map<String, ModuleConfig> loadModules() {
		Map<String, ModuleConfig> ret = new HashMap<String, ModuleConfig>();
		String[] list = Config.get().getModules();
		for (String strUri : list) {
			String path = strUri + Config.get().getModuleConfigFileName();
			ModuleConfig mc = loadModule(path);
			moduleConfigPathMap.put(mc.getName(), path);
			ret.put(mc.getName(), mc);
		}

		return ret;
	}

	/**
	 * 根据模块配置文件路径加载模块
	 * 
	 * @param moduleConfigPath
	 * @return
	 */
	private ModuleConfig loadModule(String moduleConfigPath) {
		ModuleConfig ret = null;
		try {
			URI uri = new URI(moduleConfigPath);
			IResource resource = ResourceCenter.getResource(uri);
			if (resource != null) {
				InputStream in = null;
				try {
					in = resource.openInputStream();
					ret = (ModuleConfig) XmlConfigFactory.get().load(in);
				} catch (IOException e) {
					CatchException.take(e);
				} finally {
					if (in != null) {
						try {
							in.close();
						} catch (IOException e) {
							CatchException.take(e);
						}
					}
				}
			}
		} catch (URISyntaxException e) {
			CatchException.take(e);
		}

		return ret;
	}

	public class CacheEntry {
		public String moduleName;
		public Collection<EntityConfig> entityConfigList;
		public Collection<PartConfig> partConfigList;
		public Map<String, Class<? extends IEntity>> entityClassList;
		public Map<String, Class<? extends IPart>> partClassList;
	}
}
